package web

import (
	"github.com/ecodeclub/ekit/slice"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"golang.org/x/sync/errgroup"
	intrv1 "jk-time/webook/api/proto/gen/intr/v1"
	rewardv1 "jk-time/webook/api/proto/gen/reward/v1"
	"jk-time/webook/internal/client"
	"jk-time/webook/internal/domain"
	"jk-time/webook/internal/service"
	"jk-time/webook/internal/web/jwt"
	"jk-time/webook/internal/web/vo"
	"jk-time/webook/pkg/ginx"
	"jk-time/webook/pkg/logger"
	"net/http"
	"strconv"
	"time"
)

type ArticleHandler struct {
	l      logger.Logger
	artSrv service.ArticleService

	intSrv  client.InteractiveSerivceClient
	rankSrv service.RankingService
	reward  rewardv1.PaymentSerivceClient
	biz     string
}

func NewArticleHandler(l logger.Logger, artSrv service.ArticleService, intSrv client.InteractiveSerivceClient,
	rankSrv service.RankingService) *ArticleHandler {
	return &ArticleHandler{
		l:       l,
		artSrv:  artSrv,
		intSrv:  intSrv,
		rankSrv: rankSrv,
		biz:     "article",
	}
}
func NewArticleHandlerV1(artSrv service.ArticleService) *ArticleHandler {
	return &ArticleHandler{
		artSrv: artSrv,
		biz:    "article",
	}
}
func (h *ArticleHandler) RegisterRouters(e *gin.Engine) {
	//作者
	g := e.Group("/articles")
	g.POST("/edit", h.Edit)
	g.POST("/withdraw", h.Withdraw)
	g.POST("/publish", h.Publish)
	g.POST("/list", h.List)        //自己创作前100
	g.GET("/detail/:id", h.Detail) //创作者在edit前，先加载自己的文章

	//读者
	p := g.Group("/pub")
	p.GET("/:id", h.PubDetail)
	p.POST("/like", h.Like)
	p.POST("/collect", h.Collect)
	p.GET("/ranking", h.Ranking)

	p.POST("/reward", ginx.WrapBodyAndToken[RewardReq, jwt.UserClaims](h.Reward))
}
func (h *ArticleHandler) Edit(ctx *gin.Context) {

	var req Req
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}
	id, err := h.artSrv.Save(ctx, domain.Article{
		Id:      req.Id,
		Title:   req.Title,
		Content: req.Content,
		Author: domain.Author{
			Id: claims.Uid,
		},
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("编辑帖子失败", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: id,
		Msg:  "OK",
	})
}
func (h *ArticleHandler) Publish(ctx *gin.Context) {
	var req Req
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}
	id, err := h.artSrv.Publish(ctx, req.toDomain(claims.Uid))
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("发表失败", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: id,
		Msg:  "OK",
	})
}
func (h *ArticleHandler) Withdraw(ctx *gin.Context) {
	type Req struct {
		Id int64 `json:"id"`
	}
	var req Req
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}
	err := h.artSrv.Withdraw(ctx, domain.Article{
		Id:     req.Id,
		Status: domain.ArticleStatusPrivate,
		Author: domain.Author{
			Id: claims.Uid,
		},
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("撤回帖子失败", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}
func (h *ArticleHandler) List(ctx *gin.Context) {
	type ListReq struct {
		Offset int `json:"offset"`
		Limit  int `json:"limit"`
	}
	var req ListReq
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}

	res, err := h.artSrv.List(ctx, claims.Uid, req.Offset, req.Limit)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "输入有误",
		})
		zap.L().Error("获取列表数据失败,",
			zap.Int64("uid", claims.Uid),
			zap.Int("offset", req.Offset),
			zap.Int("limit", req.Limit))
		return
	}

	//在列表页只显示一个摘要
	ctx.JSON(http.StatusOK, Result{
		Data: slice.Map[domain.Article, vo.ArticleVO](res, func(idx int, src domain.Article) vo.ArticleVO {
			return vo.ArticleVO{
				Id:       src.Id,
				Title:    src.Title,
				Abstract: src.Abstract(),
				Status:   src.Status.ToUint8(),
				Ctime:    src.Ctime.Format(time.DateTime),
				Utime:    src.Utime.Format(time.DateTime),
			}
		}),
	})
}
func (h *ArticleHandler) Detail(ctx *gin.Context) {
	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}
	id, err := strconv.ParseInt(ctx.Param("id"), 10, 10)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "参数有误",
		})
		zap.L().Error("参数有误:", zap.Error(err))
		return
	}
	art, err := h.artSrv.GetById(ctx, id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "输入有误",
		})
		zap.L().Error("获取文章信息失败:", zap.Error(err))
		return
	}
	if art.Author.Id != claims.Uid {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "输入有误",
		})
		zap.L().Error("非法访问文章，创作者ID不匹配")
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: vo.ArticleVO{
			Id:      art.Id,
			Title:   art.Title,
			Content: art.Content,
			Status:  art.Status.ToUint8(),
			Ctime:   art.Ctime.Format(time.DateTime),
			Utime:   art.Utime.Format(time.DateTime),
		},
	})
}

func (h *ArticleHandler) PubDetail(ctx *gin.Context) {
	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}
	id, err := strconv.ParseInt(ctx.Param("id"), 10, 10)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "参数有误",
		})
		zap.L().Error("参数有误:", zap.Error(err))
		return
	}

	var (
		eg   errgroup.Group
		art  domain.PublishedArticle
		intr *intrv1.GetResponse
	)
	eg.Go(func() error {
		var er error
		art, er = h.artSrv.GetPublishById(ctx, id)
		return er
	})
	eg.Go(func() error {
		var er error
		intr, er = h.intSrv.Get(ctx, &intrv1.GetRequest{Biz: h.biz, BizId: id, Uid: claims.Uid})
		return er
	})

	err = eg.Wait()
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("获取文章信息失败:", zap.Error(err))
		return
	}
	//增加阅读计数
	go func() {
		_, er := h.intSrv.IncrReadCnt(ctx, &intrv1.IncrReadCntRequest{Biz: h.biz, BizId: art.Id})
		if er != nil {
			zap.L().Error("增加阅读计数失败", zap.Int64("aid", art.Id), zap.Error(er))
		}
	}()
	ress := Result{
		Data: vo.ArticleVO{
			Id:         art.Id,
			Title:      art.Title,
			Content:    art.Content,
			AuthorName: art.Author.Name,
			Status:     art.Status.ToUint8(),
			ReadCnt:    intr.Intr.ReadCnt,
			LikeCnt:    intr.Intr.LikeCnt,
			CollectCnt: intr.Intr.CollectCnt,
			Collected:  intr.Intr.Collected,
			Liked:      intr.Intr.Liked,
			Ctime:      art.Ctime.Format(time.DateTime),
			Utime:      art.Utime.Format(time.DateTime),
		},
	}
	ctx.JSON(http.StatusOK, ress)
}
func (h *ArticleHandler) Like(ctx *gin.Context) {
	var req LikeReq
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}

	var err error
	if req.Like {
		_, err = h.intSrv.Like(ctx, &intrv1.LikeRequest{Biz: h.biz, BizId: req.Id, Uid: claims.Uid})
	} else {
		_, err = h.intSrv.CancelLike(ctx, &intrv1.CancelLikeRequest{Biz: h.biz, BizId: req.Id, Uid: claims.Uid})
	}

	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("点赞失败:", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}
func (h *ArticleHandler) Collect(ctx *gin.Context) {
	var req CollectReq
	//解析错了，写回400的错误
	if err := ctx.BindJSON(&req); err != nil {
		zap.L().Info("反序列化请求失败:", zap.Error(err))
		return
	}

	val, _ := ctx.Get("claims")
	claims, ok := val.(*jwt.UserClaims)
	if !ok {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Info("未发现用户的session信息")
		return
	}
	_, err := h.intSrv.Collect(ctx, &intrv1.CollectRequest{Biz: h.biz, BizId: req.Id, Cid: req.Cid, Uid: claims.Uid})

	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("收藏失败:", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}
func (h *ArticleHandler) Ranking(ctx *gin.Context) {
	pubs, err := h.rankSrv.TopN1(ctx)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("获取榜单失败:", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: pubs,
	})
}
func (h *ArticleHandler) Reward(ctx *gin.Context, req RewardReq, uc jwt.UserClaims) (ginx.Result, error) {
	art, err := h.artSrv.GetPublishById(ctx, req.Id)
	if err != nil {
		h.l.Error("获取作品信息失败：", logger.Error(err))
		return ginx.Result{Msg: "系统错误"}, err
	}

	//拿到二维码
	res, err := h.reward.PreReward(ctx, &rewardv1.PreRewardRequest{
		Biz:      "article",
		BizId:    req.Id,
		Uid:      uc.Uid,
		Amt:      req.Amt,
		TargetId: art.Author.Id,
		BizName:  art.Author.Name,
	})
	if err != nil {
		h.l.Error("获取二维码失败：", logger.Error(err))
		return ginx.Result{Msg: "系统错误"}, err
	}
	return ginx.Result{
		Data: map[string]any{
			"codeURL": res.CodeUrl,
		},
	}, nil
}

// -> h.reward -> payment 生成二维码且reward、payment入库
type RewardReq struct {
	Id  int64 `json:"id"`
	Amt int64 `json:"amt"`
}
type LikeReq struct {
	Id   int64 `json:"id"`
	Like bool  `json:"like"`
}
type CollectReq struct {
	Id  int64 `json:"id"`
	Cid int64 `json:"cid"`
}

type Req struct {
	Id      int64  `json:"id"`
	Title   string `json:"title"`
	Content string `json:"content"`
}

func (r Req) toDomain(uid int64) domain.Article {
	return domain.Article{
		Id:      r.Id,
		Title:   r.Title,
		Content: r.Content,
		Author: domain.Author{
			Id: uid,
		},
	}
}
